﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using GCTrackableRace.Interfaces;
using GcTrackableRace.Entities;
using GcTrackableRace.ScoreCalculator.Helpers;

namespace GcTrackableRace.ScoreCalculator.Rules
{
	public abstract class BaseAllDropsAndVisitsInKmRule : BaseRule, IRaceRule
	{
		protected BaseAllDropsAndVisitsInKmRule(string reportColumnName, string startDate, string endDate, string pointAllocation, IGeocacheInfo geocacheInfo)
			:base(reportColumnName, startDate, endDate, pointAllocation)
		{
			GeocacheInfo = geocacheInfo;
		}
  
		private IGeocacheInfo GeocacheInfo { get; set; }

		protected Tuple<double, List<string>> Calculation(ITrackable trackable, IEnumerable<ITrackableLog> trackableLogs, bool IsInKm)
		{
			var allDroppedAndVisitedLogs = (
				from log in trackableLogs
				where (
					(log.LogType == LogType.DroppedOff || log.LogType == LogType.Visited)
					&&
						(
							log.Date >= StartDate
							&&
							log.Date <= EndDate
						)
					)
				select log)
				.OrderBy(logs => logs.Date)
				.ThenBy(logs => logs.LogId)
				.ToList();

			double totalDistance = 0;

			double prevLat = 0;
			double prevLon = 0;

			var allCachesDroppedInto = new List<string>();

			foreach (var log in allDroppedAndVisitedLogs)
			{
				if (prevLat == 0)
				{
					prevLat = (double) log.Latitude;
					prevLon = (double) log.Longitude;
					
					allCachesDroppedInto.Add(
						String.Format("{0}({1}{2})",
							log.LocationGcCode,
							log.LogType.Substring(0, 1),
							GeocacheInfo.GetDetail(log.LocationGcCode).IsEarthCache ? "E" : ""));
				}
				else
				{
					var lat = Double.Parse(log.Latitude.ToString());
					var lon = Double.Parse(log.Longitude.ToString());

					if (IsInKm)
					{
						totalDistance += Haversine.InKilometers(
							prevLat, prevLon,
							lat, lon
						);
					}
					else
					{
						totalDistance += Haversine.InMiles(
							prevLat, prevLon,
							lat, lon
						);
					}

					var detail = GeocacheInfo.GetDetail(log.LocationGcCode);

					var cacheCode =
						detail == null ? "?" : 
						detail.IsEarthCache ? "E" : "";

					allCachesDroppedInto.Add(
						String.Format("{0}({1},{2})",
							log.LocationGcCode,
							log.LogType.Substring(0, 1),
							cacheCode));
					
					prevLat = lat;
					prevLon = lon;
				}
			}

			return new Tuple<double, List<string>>(totalDistance, allCachesDroppedInto);
		}
	}
}